#include "json.hpp"
#include "mqttClient.hpp"
#include "msg.hpp"
#include <algorithm>
#include <chrono>
#include <iostream>
#include <thread>

static const std::string devModbusRlistTopic = "dev/dev_rs485/sys_agent/rlist";
static const std::string devReadAckTopic = "dev/+/+/rack";
static const std::string rlistAckMsg = R"({
    "tick" : 1,
    "time" : 12345,
    "src" : "sys_agent",
    "dev" : {
        "list" : {
            "offset" : 0,
            "count" : 32,
            "type" : "modbus_485",
            "count_max": 1
        },
        "devices": [
            {
                "name": "nb20-01",
                "uuid": "abcdbabbdada",
                "id": 10001,
                "type" : "modbus_485",
                "model" : "NB20-LE",
                "profile":"/home/test/1.yaml",
                "param" : {
                    "address" : 100,
                    "baudrate": 19200,
                    "bytesize": 8,
                    "stopbits": 1,
                    "port" : "/dev/ttyS7",
                    "parity":"E"
                }
            }
        ]
    }
})";

auto mqtt = new MqttClient("test");

struct List_t {
    std::size_t offset;
    std::size_t count;
    std::string type;

    friend void from_json(const json &j, List_t &l)
    {
        if (j.contains("offset")) {
            j.at("offset").get_to(l.offset);
        }

        if (j.contains("count")) {
            j.at("count").get_to(l.count);
        }

        if (j.contains("type")) {
            j.at("type").get_to(l.type);
        }
    }
};

struct Dev_t {
    List_t list;

    friend void from_json(const json &j, Dev_t &d)
    {
        if (j.contains("list")) {
            j.at("list").get_to(d.list);
        }
    }
};

struct RlistMsg_t : public Protocol::Msg_t {
    Dev_t dev;

    friend void from_json(const json &j, RlistMsg_t &r)
    {
        if (j.contains("tick")) {
            j.at("tick").get_to(r.tick);
        }

        if (j.contains("time")) {
            j.at("time").get_to(r.time);
        }

        if (j.contains("src")) {
            j.at("src").get_to(r.src);
        }

        if (j.contains("dev")) {
            j.at("dev").get_to(r.dev);
        }
    }

    friend std::ostream &operator<<(std::ostream &out, const RlistMsg_t &r)
    {
        out << "tick --> " << r.tick << std::endl;
        out << "time --> " << r.time << std::endl;
        out << "src --> " << r.src << std::endl;
        out << "offset --> " << r.dev.list.offset << std::endl;
        out << "count --> " << r.dev.list.count << std::endl;
        out << "type --> " << r.dev.list.type;

        return out;
    }
};

bool parseMqttMsg(const Msg_t &m)
{
    if (m.topic.find("rlist")) {
        auto r = json::parse(m.payload).get<RlistMsg_t>();
        // std::cout << r << std::endl;

        if (r.dev.list.type == "modbus_485") {
            mqtt->publish("dev/sys_agent/dev_rs485/rlistack", rlistAckMsg);
        }
    }

    return true;
}

int main(int argc, char **argv)
{
    std::cout << "hello,world" << std::endl;

    mqtt->connect("localhost", 1883);

    mqtt->registerOnConnected(std::bind(&MqttClient::subscribe, mqtt, devModbusRlistTopic));
    mqtt->registerOnConnected(std::bind(&MqttClient::subscribe, mqtt, devReadAckTopic));

#if 0
    std::thread([&] {
        while (true) {
            std::this_thread::sleep_for(std::chrono::milliseconds(50));
            mqtt->publish("dev/rule_basic/10001/read", R"({
    "tick": 1,
    "time": 123,
    "src" : "rule_basic",
    "dev": {
        "devices": [
            {
                "id": 10001,
                "points": [
                    { "key": "Ua" }
                ]
            }
        ]
    }
})");
            // std::cout << "send topic ok" << std::endl;
        }
    }).detach();
#endif

#if 0
    std::thread([&] {
        while (true) {
            std::this_thread::sleep_for(std::chrono::seconds(10));
            mqtt->publish("dev/rule_basic/10001/write", R"({
    "tick": 1,
    "time": 123,
    "src" : "rule_basic",
    "dev": {
        "devices": [
            {
                "id": 10001,
                "points": [
                    { "key": "Ua", "val": 1000 }
                ]
            }
        ]
    }
})");
            std::cout << "send write topic ok" << std::endl;
        }
    }).detach();

#endif

#if 0
    std::thread([&] {
        while (true) {
            std::this_thread::sleep_for(std::chrono::seconds(3));
            mqtt->publish("dev/rule_basic/10001/write", R"({
    "tick": 1,
    "time": 123,
    "src" : "rule_basic",
    "dev": {
        "devices": [
            {
                "id": 10001,
                "points": [
                    { "key": "Unlock", "val": 2 },
                    { "key": "SwOpen", "val": 6 }
                ]
            }
        ]
    }
})");

            std::this_thread::sleep_for(std::chrono::seconds(3));

            mqtt->publish("dev/rule_basic/10001/write", R"({
    "tick": 1,
    "time": 123,
    "src" : "rule_basic",
    "dev": {
        "devices": [
            {
                "id": 10001,
                "points": [
                    { "key": "Unlock", "val": 2},
                    { "key": "SwClose", "val": 7}
                ]
            }
        ]
    }
})");
            std::cout << "send write topic ok" << std::endl;
        }
    }).detach();
#endif

#if 0
    std::this_thread::sleep_for(std::chrono::seconds(10));
    mqtt->publish("dev/web/dev_rs485/autoadd", R"(
        {
    "tick": 1,
    "time": 1234,
    "src": "web",
    "dev": {
        "type":"autoaddstart"
    }
}
    )");
#endif

    while (true) {
        auto msg = mqtt->getOneItem();
        parseMqttMsg(msg);
    }

    return 0;
}
